#include "stdafx.h"
#include "CheckCode.h"
#include "Decode.h"



//************************************
// Method:    	check
// FullName:  	CheckCode::check
// Access:    	public 
// Returns:   	void
// Description:	Main method to call
//************************************
void CheckCode::check(std::string filename, int part)
{	
	checkParity(filename, part);
	//int check = checkData(filename, part);
/*

	if(Init::getDebug()<2)
	{
		cout<<check<<endl;
	}*/
		
}



//************************************
// Method:    	checkParity
// FullName:  	CheckCode::checkParity
// Access:    	private static 
// Returns:   	void
// Description:	checks first 5 bits of nibble against paritybit
//				calls correction function if != 
//************************************
void CheckCode::checkParity(std::string filename, int part)
{
	int add;

	//runns through all rows
	for (int i=0; i<96; i++)
	{
		add=0;

		//calculating parity
		for (int j=0; j<5; j++)
		{
			add=(add + Decode::getValue(i,j))%2;
		}

		//flip weakest bit if given parity != calculated parity
		if(add !=  Decode::getValue(i,5))
		{
			flipBitRow(i);
			std::stringstream data;
			data<<"Paritybit-Error in line "<<i;
			/*std::string error="Paritybit-Error in line "
			error+=i;*/
			errorOutput(filename,part,data.str());
			Decode::setValue(i,5,0);
		}
		else 
			Decode::setValue(i,5,1);
	}


}



//************************************
// Method:    	flipBitRow
// FullName:  	CheckCode::flipBitRow
// Access:    	private static 
// Returns:   	void
// Parameter: 	int i
// Description:	flips bit in row with min. probability to be correct
//************************************
void CheckCode::flipBitRow(int i)
{
	//get probability-factor of 2. bit (first information bit)
	float prob = abs(Decode::getValue(i,1)-1)+Decode::getPossibility(i,1);
	int lowest = 1;
	//running through bit 3-6 to find weakest point
	for (int j=2; j<6; j++)
	{
		if (abs(Decode::getValue(i,j)-1)+Decode::getPossibility(i,j) < prob)
		{
			prob = Decode::getPossibility(i,j);
			lowest=j;
		}
	}
		//flip weakest bit
	if (Decode::getValue(i,lowest)==0)
		Decode::setValue(i,lowest,1);
	else
		Decode::setValue(i,lowest,0);
}




//************************************
// Method:    	flipBitCol
// FullName:  	CheckCode::flipBitCol
// Access:    	private static 
// Returns:   	void
// Parameter: 	int start
// Parameter: 	int end
// Parameter: 	int checkcode
// Parameter: 	int col
// Parameter: 	int start2
// Parameter: 	int end2
// Description:	flips bit in given collumn with lowest probability to be correct
//************************************
void CheckCode::flipBitCol(int start, int end, int checkcode, int col, int start2, int end2)
{
	col=sqrt((float)col);
	float prob = Decode::getPossibility(checkcode,col+1);
	int lowest = start;
	
	//running through given rows, checking given bits strength
	for (int i=0; i<end+1;i++)
	{
		if (Decode::getPossibility(start+i,col+1)<prob)
		{
			prob = Decode::getPossibility(start+i,col+1);
			lowest = start+i;
		}
	}

	//Checksum 5 contains speed and heading, which are stored separately.
	//So here the check continues for the second datablock 
	if (start2!=0 && end2!=0)
	{
		for (int i=0; i<end2+1; i++)
		{
			if (Decode::getPossibility(start2+i,col+1)<prob)
			{
				prob = Decode::getPossibility(start2+i,col+1);
				lowest = start2+i;
			}
		}
	}

	//flipping weakest bit
	if (Decode::getValue(lowest,col+1)==0)
			Decode::setValue(lowest,col+1,1);
	else
			Decode::setValue(lowest,col+1,0);
}





//************************************
// Method:    	falseBit
// FullName:  	CheckCode::falseBit
// Access:    	private static 
// Returns:   	void
// Parameter: 	int start
// Parameter: 	int end
// Parameter: 	int checkcode
// Parameter: 	int col
// Parameter: 	int start2
// Parameter: 	int end2
// Description:	flips false bit
//************************************
void CheckCode::falseBit(int start, int end, int checkcode, int col, int start2, int end2)
{
	for (int i=3; i>=0; i--)
	{
		if (col >= pow(2.f,i))
		{
			col-=pow(2.f,i);
			flipBitCol(start, end, checkcode, i, start2, end2);
		}
	}
}




//************************************
// Method:    	checkData
// FullName:  	CheckCode::checkData
// Access:    	private static 
// Returns:   	int
// Description:	checks datablocks against corresponding Checksum
//************************************
int CheckCode::checkData(std::string filename, int part)
{
	int errorcode=0;
	int checksum=0;
	std::string error;
	//Latitude
	if ((checksum=check(9,15,90))!=0)
	{
		error="Checksum-Error: Latitude";
		errorOutput(filename,part,error);
		errorcode -=1;
		falseBit(9,15,90,checksum);
	}

	//Longitude
	if((checksum=check(16,23,91))!=0)
	{
		error="Checksum-Error: Longitude";
		errorOutput(filename,part,error);
		errorcode -=2;
		falseBit(16,23,91,checksum);
	}

	//Time
	if ((checksum=check(1,8,92))!=0)
	{
		error="Checksum-Error: Time";
		errorOutput(filename,part,error);
		errorcode -=4;
		falseBit(1,8,92,checksum);
	}

	//Altitude
	if ((checksum=check(33,38,93))!=0)
	{
		error="Checksum-Error: Altitude";
		errorOutput(filename,part,error);
		errorcode -=8;
		falseBit(33,38,93,checksum);
	}

	//Speed&Heading
	if ((checksum=check(39,43,94,59,63))!=0)
	{
		error="Checksum-Error: Speed/Heading";
		errorOutput(filename,part,error);
		errorcode -=16;
		falseBit(39,43,94,checksum,59,63);
	}

	if (errorcode!=0)
		Debug::message("ERRORCODE: "+errorcode,Debug::IMPORTANT);

	return errorcode;
}



//************************************
// Method:    	check
// FullName:  	CheckCode::check
// Access:    	public 
// Returns:   	int
// Parameter: 	int start
// Parameter: 	int end
// Parameter: 	int chk
// Parameter: 	int start2
// Parameter: 	int end2
// Description:	checks datablock against corresponding Checksum
//************************************
int CheckCode::check(int start, int end, int chk, int start2, int end2)
{
	int check = 0;
	int add = 0;
	int carry = 0;
	int nibble[]={0,0,0,0};
	int data[4];
	int sum;

	//adding rows to get checksum
	for (int i=start; i<end; i++)
	{
		for (int j=1; j<=4; j++)
			data[j-1]=Decode::getValue(i,j);

		binAdd(nibble,data);
	}

	//further adding for checksum5 - see flipBitCol()
	if (start2!=0 && end2!=0)
	{
		for (int i=start2; i<end2; i++)
		{
			for (int j=1; j<=4; j++)
				data[j-1]=Decode::getValue(i,j);
	
			binAdd(nibble,data);
		}
	}

	//check calculated checksum against given checksum
	for (int i=0; i<4; i++)
	{
		if (nibble[i]!=Decode::getValue(chk,i+1))
			check+=pow(2.f,i);
	}

	return check;

}



//************************************
// Method:		binAdd
// FullName:	CheckCode::binAdd
// Access:		private static 
// Returns:		void
// Parameter:	int * code1
// Parameter:	int code2[4]
// Description: adds code2 to code1
//************************************
void CheckCode::binAdd(int *code1, int code2[4])
{
	int carry = 0;
	int add;

	//add bit by bit
	for (int j=3; j>=0; j--)
	{
		add = code1[j] + code2[j] + carry;
		switch(add)
		{
			case 3:
				code1[j]=1;
				carry=1;
				break;
			case 2:
				code1[j]=0;
				carry=1;
				break;
			case 1:
				code1[j]=1;
				carry=0;
				break;
			case 0:
				code1[j]=0;
				carry=0;
				break;
		}
	}
	
	//if last carrybit = 1, add 0001 to added code
	if (carry == 1)
	{
		int car[]={0,0,0,1};
		binAdd(code1, car);
	}

}


//************************************
// Method:    	errorOutput
// FullName:  	CheckCode::errorOutput
// Access:    	private static 
// Returns:   	void
// Parameter: 	std::string filename
// Parameter: 	int part
// Parameter: 	std::string error
// Description:	If error occured, write it to file
//************************************
void CheckCode::errorOutput(std::string filename, int part, std::string error)
{
	if(!Init::getErrorHappened())
		Init::initErrorFile();
	

	time_t t;
	struct tm *met;
	char timestamp[80];
	std::string st(Init::getPath());
	std::stringstream data;
	ofstream output;

	//replace "\" with "/"
	for (int i=0; i<st.size(); i++)
	{
		if (st[i]=='\\')
			st[i]='/';
	}
	//appending all the stuff for a valid path
	st+=Init::getDataDir();
	st+="/error.csv";
	output.open(st.c_str(),ios::app);

	time(&t);
	met = localtime(&t);
	strftime(timestamp,80,"%d.%m.%y  %H:%M",met);

	data<<filename<<","<<part<<","<<error<<","<<timestamp;
	output<<data.str()<<endl;
	output.close();
}